package com.gzist.aidemo.service;

import cn.hutool.core.convert.Convert;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gzist.aidemo.config.AIConfig;
import com.gzist.aidemo.entity.DTO.ImageRequestDTO;
import com.gzist.aidemo.entity.DTO.WsMessage;
import com.gzist.aidemo.entity.model.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Flux;
import reactor.netty.http.client.PrematureCloseException;
import reactor.util.retry.Retry;

import java.io.PrintWriter;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeoutException;


/**
 * AI服务类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AIService {
    private final AIConfig config;
    private final WebClient.Builder webClientBuilder;
    private final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 处理聊天WebSocket连接，用于接收用户消息并返回相应的回答
     * 如果这是用户的新会话，会首先发送一个模板提示
     * 该方法使用React式编程模型，通过Flux发布者异步处理消息
     *
     * @param message 客户端传过来的消息体
     * @param userId  用户唯一标识符
     * @return 返回一个Flux发布者，它将异步地发送聊天响应
     */
    public Flux<String> handleChatWebSocket(String message, String userId) {
        // 处理用户的消息
        return sendChatRequest(userId, message)
                .doOnNext(response -> log.info("发送响应: {}", response))
                .onErrorResume(e -> {
                    log.error("WebSocket通信错误: {}", e.getMessage());
                    return Flux.just("抱歉，服务器处理请求时发生错误，请稍后重试。");
                });
    }

    /**
     * 向指定用户发送聊天请求
     * 该方法负责构建聊天请求，发送给API，并处理响应
     *
     * @param userId  用户ID，用于从会话历史记录中获取该用户的聊天记录
     * @param message 客户端传过来的消息体
     * @return 返回一个Flux流，包含可能的聊天回复
     */
    private Flux<String> sendChatRequest(String userId, String message) {
        List<Message> messageList;
        WsMessage wsMessage;
        try {
            wsMessage = JSONUtil.toBean(message, WsMessage.class);
        } catch (Exception e) {
            log.error("解析消息时发生错误: {}", e.getMessage());
            return Flux.just("抱歉，服务器处理请求时发生错误，请检查消息格式是否正确。");
        }
        if (wsMessage == null) {
            return Flux.just("抱歉，服务器处理请求时发生错误，请稍后重试。");
        }

        // 构建聊天请求对象
        ChatRequest request = new ChatRequest();
        messageList = wsMessage.getMessageList();
        if (messageList != null) {
            log.info("从message中获取所有信息成功:{}", messageList);
            request.setMessages(messageList);
        }
        if (wsMessage.getModel() != null) {
            request.setModel(wsMessage.getModel());
        }
        if (wsMessage.getMaxTokens() != null) {
            try {
                request.setMax_tokens(Convert.toInt(wsMessage.getMaxTokens()));
            } catch (Exception e) {
                log.error("转换max_tokens时发生错误: {}", e.getMessage());
                request.setMax_tokens(2000); // 提供默认值
            }
        }

        // 记录发送请求的日志
        log.info("发送请求到: {}", config.getApiChatUrl());
        log.info("发送请求: {}", request);
        // 使用WebClient发送POST请求到API，并处理响应
        return webClientBuilder.build()
                .post()
                .uri(config.getApiChatUrl())
                .header("Authorization", "Bearer " + config.getApiKey())
                .header("Content-Type", "application/json")
                .bodyValue(request)
                .retrieve()
                .bodyToFlux(String.class)
                .timeout(Duration.ofSeconds(300))
                .retryWhen(Retry.backoff(4, Duration.ofSeconds(2))
                        .filter(throwable ->
                                throwable instanceof PrematureCloseException
                                        || throwable instanceof TimeoutException
                                        || throwable instanceof RuntimeException)
                        .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> {
                            log.error("重试次数已用完，最后一次错误: {}", retrySignal.failure().getMessage());
                            return new RuntimeException("服务暂时不可用，请稍后重试");
                        }))
                .onErrorResume(e -> {
                    log.error("请求处理错误: {}", e.getMessage());
                    // 返回纯文本错误信息，避免后续解析失败
                    return Flux.just("抱歉，服务器处理请求时发生错误: " + e.getMessage());
                })
                .map(response -> {
                    try {
                        if ("[DONE]".equals(response)) {
                            return "\n";
                        }
                        // 检查响应是否为JSON格式
                        if (response == null || !response.trim().startsWith("{")) {
                            log.warn("API返回的响应不是JSON格式: {}", response);
                            return "";
                        }

                        JsonNode jsonNode = objectMapper.readTree(response);
                        if (jsonNode == null) {
                            log.warn("API返回的响应为空或格式不正确: {}", response);
                            return "";
                        }

                        JsonNode choices = jsonNode.get("choices");
                        if (choices == null || !choices.isArray() || choices.size() == 0) {
                            log.warn("API返回的choices字段为空或格式不正确: {}", response);
                            return "";
                        }

                        JsonNode choice = choices.get(0);
                        JsonNode delta = choice.get("delta");
                        if (delta == null || !delta.has("content")) {
                            log.warn("API返回的delta字段为空或格式不正确: {}", response);
                            return "";
                        }

                        return delta.get("content").asText();
                    } catch (Exception e) {
                        log.error("解析响应时出错: {}", e.getMessage());
                        log.error("原始响应: {}", response);
                        return "";
                    }
                })
                .filter(content -> content != null && !content.isEmpty() && !content.equals("null")); // 过滤掉null和空字符串
    }

    /**
     * 获取完整的 AI 响应内容
     *
     * @param wsMessage 客户端发送的消息体
     * @return AI 模型的完整响应内容
     */
    public String getCompleteChatResponse(WsMessage wsMessage) {
        // 构建聊天请求对象
        ChatRequest request = new ChatRequest();
        List<Message> messageList = wsMessage.getMessageList();
        if (messageList != null) {
            request.setMessages(messageList);
        }
        if (wsMessage.getModel() != null) {
            request.setModel(wsMessage.getModel());
        }
        if (wsMessage.getMaxTokens() != null) {
            try {
                request.setMax_tokens(Convert.toInt(wsMessage.getMaxTokens()));
            } catch (Exception e) {
                log.error("转换max_tokens时发生错误: {}", e.getMessage());
                request.setMax_tokens(2000); // 提供默认值
            }
        }

        // 设置 stream 为 false，确保返回完整响应
        request.setStream(false);

        // 记录发送请求的日志
        log.info("发送请求到: {}", config.getApiChatUrl());
        log.info("发送请求: {}", request);

        // 使用 WebClient 发送 POST 请求到 API，并处理响应
        return webClientBuilder.build()
                .post()
                .uri(config.getApiChatUrl())
                .header("Authorization", "Bearer " + config.getApiKey())
                .header("Content-Type", "application/json")
                .bodyValue(request)
                .retrieve()
                .bodyToMono(String.class)
                .map(response -> {
                    try {
                        JsonNode jsonNode = objectMapper.readTree(response);
                        JsonNode choices = jsonNode.get("choices");
                        if (choices != null && choices.isArray() && choices.size() > 0) {
                            JsonNode choice = choices.get(0);
                            JsonNode message = choice.get("message");
                            if (message != null && message.has("content")) {
                                return message.get("content").asText();
                            }
                        }
                        return "无法解析响应内容";
                    } catch (Exception e) {
                        log.error("解析响应时出错: {}", e.getMessage());
                        return "解析响应时出错";
                    }
                })
                .block(); // 阻塞等待完整响应
    }


    /**
     * 调用 DeepSeek 文生图 API 生成图片
     *
     * @param imageRequestDTO 接口参数，包括图片描述的文本提示词，图片尺寸，输出图片数量等
     * @return 图片的 URL 地址集合
     */
    public List<String> generateImage(ImageRequestDTO imageRequestDTO) {
        ImageGenerationRequest request = new ImageGenerationRequest();
        request.setPrompt(imageRequestDTO.getPrompt());
        if (imageRequestDTO.getImageSize() != null) {
            request.setImage_size(imageRequestDTO.getImageSize());
        }
        if (imageRequestDTO.getBatchSize() != null) {
            request.setBatch_size(imageRequestDTO.getBatchSize());
        }
        try {
            return webClientBuilder.build()
                    .post()
                    .uri(config.getApiImagesUrl())
                    .header("Authorization", "Bearer " + config.getApiKey())
                    .header("Content-Type", "application/json")
                    .bodyValue(request)
                    .retrieve()
                    .bodyToMono(ImageResponse.class)
                    .map(imageResponse -> {
                        List<String> imageUrls = new ArrayList<>();
                        if (imageResponse.getImages() != null) {
                            for (Image image : imageResponse.getImages()) {
                                imageUrls.add(image.getUrl());
                            }
                        }
                        return imageUrls;
                    })
                    .block(Duration.ofSeconds(120));
        } catch (Exception e) {
            log.error("生成图片时发生错误: {}", e.getMessage());
            return handleErrorResponse(e);
        }
    }


    /**
     * 处理 API 错误响应
     *
     * @param e 错误信息
     * @return 错误信息
     */
    private List<String> handleErrorResponse(Exception e) {
        ArrayList<String> result = new ArrayList<>();
        try {
            // 假设响应体为 JSON 格式
            String response = ((WebClientResponseException) e).getResponseBodyAsString();
            DeepSeekErrorResponse errorResponse = objectMapper.readValue(response, DeepSeekErrorResponse.class);
            log.error("DeepSeek API 错误: code={}, message={}, data={}", errorResponse.getCode(), errorResponse.getMessage(), errorResponse.getData());
            result.add("抱歉，服务器处理请求时发生错误: " + e.getMessage());
            return result;
        } catch (Exception ex) {
            log.error("无法解析 API 错误响应: {}", ex.getMessage());
            result.add("抱歉，服务器处理请求时发生错误: " + e.getMessage());
            return result;
        }
    }


    /**
     * 将内容保存到文件中
     * 此方法用于将给定的内容写入到文件中，根据内容是否为问题，选择不同的格式进行写入
     *
     * @param fileWriter PrintWriter对象，用于将内容写入文件
     * @param content    要写入文件的内容
     * @param isQuestion 标志内容是否为问题，如果是问题，则为true；否则为false
     */
    private void saveToFile(PrintWriter fileWriter, String content, boolean isQuestion) {
        // 获取当前时间戳，格式为“年-月-日 时:分:秒”
        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        // 根据isQuestion参数决定是否以问题的格式写入内容
        if (isQuestion) {
            // 如果是问题，以“[时间戳] Question:\n内容\n\n[时间戳] Answer:\n”的格式写入
            fileWriter.printf("\n[%s] Question:\n%s\n\n[%s] Answer:\n", timestamp, content, timestamp);
        } else {
            // 如果不是问题，直接写入内容
            fileWriter.print(content);
        }
        // 刷新PrintWriter，确保内容及时写入文件
        fileWriter.flush();
    }

}